/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package analyzer;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Time;
import java.util.Date;
import java.util.Enumeration;
import javax.swing.tree.DefaultMutableTreeNode;
import jpcap.packet.*;

/**
 *
 * @author lbl
 */
public class PacketAnalyzer {

    private Packet packet;
    private DefaultMutableTreeNode rootNode;
    private int num;
    private long time;
    private String source;
    private String destination;
    private String protocol;

    /**
     *
     * @param packet
     * @param num
     */
    public PacketAnalyzer(Packet packet, int num) {
        this.packet = packet;
        this.rootNode = new DefaultMutableTreeNode("Packet Information");
        this.num = num;
        this.source = "Unknown";
        this.destination = "Unknown";
        this.protocol = "Unknown";

    }

    /**
     *
     * @return
     */
    public Packet getPacket() {
        return this.packet;
    }

    /**
     *
     * @return
     */
    public Integer getNum() {
        return this.num;
    }

    /**
     *
     * @return
     */
    public Object getTime() {
        return new Time(packet.sec * 1000 + packet.usec / 1000);
    }

    /**
     *
     * @return
     */
    public String getSource() {
        return this.source;
    }

    /**
     *
     * @return
     */
    public String getDestination() {
        return this.destination;
    }

    /**
     *
     * @return
     */
    public String getProtocol() {
        return this.protocol;
    }

    /**
     *
     * @return
     */
    public byte[] getHeader() {
        return this.packet.header;
    }

    /**
     *
     * @return
     */
    public byte[] getData() {
        return this.packet.data;
    }

    private int Byte2int(byte b) {
        return b < 0 ? b + 256 : b;
    }

    private String Byte2hex(byte b) {
        int a = Byte2int(b);
        final char[] HEX_NUM = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        return String.valueOf(HEX_NUM[a / 16]) + String.valueOf(HEX_NUM[a % 16]);
    }

    /**
     *
     * @return
     */
    public String getHexHeader() {
        String s = "";
        for (int i = 0; i < packet.header.length; i++) {
            s += Byte2hex(packet.header[i]);
        }
        return s;
    }

    /**
     *
     * @return
     */
    public String getHexData() {
        String s = "";
        for (int i = 0; i < packet.data.length; i++) {
            s += Byte2hex(packet.data[i]);
        }
        return s;
    }

    /**
     *
     * @return
     */
    public String getHex() {
        return getHexHeader() + getHexData();
    }

    /**
     *
     * @return
     */
    public String getASCIIHeader() {
        String s = "";
        for (int i = 0; i < packet.header.length; i++) {
            if (packet.header[i] > 32 && packet.header[i] < 127) {
                s += (char) packet.header[i];
            } else {
                s += " ";
            }
        }
        return s;
    }

    /**
     *
     * @return
     */
    public String getASCIIData() {
        String s = "";
        for (int i = 0; i < packet.data.length; i++) {
            if (packet.data[i] > 32 && packet.data[i] < 127) {
                s += (char) packet.data[i];
            } else {
                s += " ";
            }
        }
        return s;
    }

    /**
     *
     * @return
     */
    public String getASCII() {
        return getASCIIHeader() + getASCIIData();
    }

    /**
     *
     * @return
     */
    public DefaultMutableTreeNode getRootNode() {
        return this.rootNode;
    }

    /**
     *
     * @return
     */
    public long getSeqNum() {
        if (packet instanceof TCPPacket) {
            TCPPacket p = (TCPPacket) packet;
            return p.sequence;
        } else {
            return 0;
        }
    }

    /**
     *
     * @return
     */
    public long getAckNum() {
        if (packet instanceof TCPPacket) {
            TCPPacket p = (TCPPacket) packet;
            return p.ack_num;
        } else {
            return 0;
        }
    }

    /**
     *
     */
    public void analyze() {
        rootNode.add(analyze(packet));
        this.time = packet.usec;
        if (packet.datalink instanceof EthernetPacket) {
            rootNode.add(analyze((EthernetPacket) packet.datalink));
            if (packet instanceof ARPPacket) {
                rootNode.add(analyze((ARPPacket) packet));
            }
            if (packet instanceof IPPacket) {
                rootNode.add(analyze((IPPacket) packet));
                if (packet instanceof ICMPPacket) {
                    rootNode.add(analyze((ICMPPacket) packet));
                }
                if (packet instanceof TCPPacket) {
                    rootNode.add(analyze((TCPPacket) packet));
                }
                if (packet instanceof UDPPacket) {
                    rootNode.add(analyze((UDPPacket) packet));
                }
            }
        }
    }

    /**
     *
     * @param sec
     * @param usec
     * @return
     */
    public String getDate(long sec, long usec) {
        return new Date(sec * 1000 + usec / 1000).toString();
    }

    private DefaultMutableTreeNode analyze(Packet packet) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode("Frame " + num);
        node.add(new DefaultMutableTreeNode("Data: " + getDate(packet.sec, packet.usec)));
        node.add(new DefaultMutableTreeNode("Frame length: " + packet.len));
        node.add(new DefaultMutableTreeNode("Capture length: " + packet.caplen));
        return node;
    }

    private String getEtherType(short s) {
        switch (s) {
            case EthernetPacket.ETHERTYPE_ARP:
                return EthernetPacket.ETHERTYPE_ARP + " - ARP";
            case EthernetPacket.ETHERTYPE_IP:
                return EthernetPacket.ETHERTYPE_IP + " - IP";
            case EthernetPacket.ETHERTYPE_IPV6:
                return EthernetPacket.ETHERTYPE_IPV6 + " - IPV6";
            case EthernetPacket.ETHERTYPE_LOOPBACK:
                return EthernetPacket.ETHERTYPE_LOOPBACK + " - Loopback";
            case EthernetPacket.ETHERTYPE_PUP:
                return EthernetPacket.ETHERTYPE_PUP + " - PUP";
            case EthernetPacket.ETHERTYPE_REVARP:
                return EthernetPacket.ETHERTYPE_REVARP + " - RARP";
            case EthernetPacket.ETHERTYPE_VLAN:
                return EthernetPacket.ETHERTYPE_VLAN + " - VLAN";
            default:
                return String.valueOf(s);
        }
    }

    private DefaultMutableTreeNode analyze(EthernetPacket packet) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode("Ethernet");
        node.add(new DefaultMutableTreeNode("Destination MAC Address: " + packet.getDestinationAddress().toUpperCase()));
        node.add(new DefaultMutableTreeNode("Source MAC Address: " + packet.getSourceAddress().toUpperCase()));
        node.add(new DefaultMutableTreeNode("EtherType: " + getEtherType(packet.frametype)));
        return node;
    }

    private String getHardType(short s) {
        switch (s) {
            case ARPPacket.HARDTYPE_ETHER:
                return s + " - Ethernet";
            case ARPPacket.HARDTYPE_IEEE802:
                return s + "Token ring";
            case ARPPacket.HARDTYPE_FRAMERELAY:
                return s + "Frame relay";
            default:
                return String.valueOf(s);
        }
    }

    private String getProtocolType(short s) {
        switch (s) {
            case ARPPacket.PROTOTYPE_IP:
                return s + " - IP";
            default:
                return String.valueOf(s);
        }
    }

    private String getOperation(short s) {
        switch (s) {
            case ARPPacket.ARP_REQUEST:
                return "ARP Request";
            case ARPPacket.ARP_REPLY:
                return "ARP Reply";
            case ARPPacket.RARP_REQUEST:
                return "Reverse ARP Request";
            case ARPPacket.RARP_REPLY:
                return "Reverse ARP Reply";
            case ARPPacket.INV_REQUEST:
                return "Identify peer Request";
            case ARPPacket.INV_REPLY:
                return "Identify peer Reply";
            default:
                return String.valueOf(s);
        }
    }

    private DefaultMutableTreeNode analyze(ARPPacket packet) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode("ARP");
        this.protocol = "ARP";
        node.add(new DefaultMutableTreeNode("Hardware type: " + getHardType(packet.hardtype)));
        node.add(new DefaultMutableTreeNode("Protocol type: " + getProtocolType(packet.prototype)));
        node.add(new DefaultMutableTreeNode("Hardware length: " + packet.hlen));
        node.add(new DefaultMutableTreeNode("Protocol length: " + packet.plen));
        node.add(new DefaultMutableTreeNode("Operation: " + getOperation(packet.operation)));
        node.add(new DefaultMutableTreeNode("Sender MAC Address: " + packet.getSenderHardwareAddress()));
        node.add(new DefaultMutableTreeNode("Target MAC Address: " + packet.getTargetHardwareAddress()));
        this.source = packet.getSenderProtocolAddress().toString().replaceFirst("/", "");
        node.add(new DefaultMutableTreeNode("Sender IP Address: " + this.source));
        this.destination = packet.getTargetProtocolAddress().toString().replaceFirst("/", "");
        node.add(new DefaultMutableTreeNode("Target IP Address: " + this.destination));
        return node;
    }

    private String getHeaderLength(byte b) {
        return String.valueOf((b & 0x0F) * 4) + " bytes";
    }

    private String getPrecedence(byte b) {
        switch (b & 0x07) {
            case 0x07:
                return "111 - Network Control";
            case 0x06:
                return "110 - Internetwork Control";
            case 0x05:
                return "101 - CRITIC/ECP";
            case 0x04:
                return "100 - Flash Override";
            case 0x03:
                return "011 - Flash";
            case 0x02:
                return "010 - Immediate";
            case 0x01:
                return "001 - Priority";
            case 0x00:
                return "000 - Routine";
            default:
                return String.valueOf(b);
        }
    }

    private String getDelay(byte b) {
        switch (b & 0x08) {
            case 0x08:
                return "1 - Low Delay";
            case 0x00:
                return "0 - Normal Delay";
            default:
                return String.valueOf(b);
        }
    }

    private String getThroughput(byte b) {
        switch (b & 0x10) {
            case 0x10:
                return "1 - High Throughput";
            case 0x00:
                return "0 - Normal Throughput";
            default:
                return String.valueOf(b);
        }
    }

    private String getReliability(byte b) {
        switch (b & 0x20) {
            case 0x20:
                return "1 - High Reliability";
            case 0x00:
                return "0 - Normal Reliability";
            default:
                return String.valueOf(b);
        }
    }

    private String getProtocol(short s) {
        switch (s) {
            case IPPacket.IPPROTO_ICMP:
                return IPPacket.IPPROTO_ICMP + " - ICMP";
            case IPPacket.IPPROTO_IGMP:
                return IPPacket.IPPROTO_IGMP + " - IGMP";
            case IPPacket.IPPROTO_TCP:
                return IPPacket.IPPROTO_TCP + " - TCP";
            case IPPacket.IPPROTO_UDP:
                return IPPacket.IPPROTO_UDP + " - UDP";
            default:
                return String.valueOf(s);
        }
    }

    private DefaultMutableTreeNode analyze(
            IPPacket packet) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode("IP");
        this.protocol = "IP";
        node.add(new DefaultMutableTreeNode("Version: " + packet.version));
        node.add(new DefaultMutableTreeNode("Header Length: " + getHeaderLength(packet.header[14])));
        DefaultMutableTreeNode tos = new DefaultMutableTreeNode("Type of Service:");
        tos.add(new DefaultMutableTreeNode("Precedence: " + getPrecedence(packet.header[15])));
        tos.add(new DefaultMutableTreeNode("Delay: " + getDelay(packet.header[15])));
        tos.add(new DefaultMutableTreeNode("Throughput: " + getThroughput(packet.header[15])));
        tos.add(new DefaultMutableTreeNode("Reliability: " + getReliability(packet.header[15])));
        node.add(tos);
        node.add(new DefaultMutableTreeNode("Total Length: " + packet.length));
        node.add(new DefaultMutableTreeNode("Identification: " + packet.ident));
        DefaultMutableTreeNode flags = new DefaultMutableTreeNode("Flags:");
        flags.add(new DefaultMutableTreeNode("Don't Fragment: " + packet.dont_frag));
        flags.add(new DefaultMutableTreeNode("More Fragments: " + packet.more_frag));
        node.add(flags);
        node.add(new DefaultMutableTreeNode("Fragment Offset: " + packet.offset));
        node.add(new DefaultMutableTreeNode("Time To Live: " + packet.hop_limit));
        node.add(new DefaultMutableTreeNode("Protocol: " + getProtocol(packet.protocol)));
        node.add(new DefaultMutableTreeNode("Source address: " + packet.src_ip.getHostAddress()));
        node.add(new DefaultMutableTreeNode("Destination address: " + packet.dst_ip.getHostAddress()));
        this.source = packet.src_ip.getHostAddress();
        this.destination = packet.dst_ip.getHostAddress();
        return node;
    }

    private String getType(byte b) {
        switch (b) {
            case ICMPPacket.ICMP_ECHOREPLY:
                return "0 - Echo Reply";
            case ICMPPacket.ICMP_UNREACH:
                return "3 - Destination Unreachable";
            case ICMPPacket.ICMP_SOURCEQUENCH:
                return "4 - Source Quench";
            case ICMPPacket.ICMP_REDIRECT:
                return "5 - Redirect";
            case ICMPPacket.ICMP_ECHO:
                return "8 - Echo";
            case ICMPPacket.ICMP_ROUTERADVERT:
                return "9 - Router Advertisement";
            case ICMPPacket.ICMP_ROUTERSOLICIT:
                return "10 - Router Solicitation";
            case ICMPPacket.ICMP_TIMXCEED:
                return "11- Time Exceeded";
            case ICMPPacket.ICMP_PARAMPROB:
                return "12 - Parameter Problem";
            case ICMPPacket.ICMP_TSTAMP:
                return "13 - Timestamp";
            case ICMPPacket.ICMP_TSTAMPREPLY:
                return "14 - Timestamp Reply";
            case ICMPPacket.ICMP_IREQ:
                return "15 - Information Request";
            case ICMPPacket.ICMP_IREQREPLY:
                return "16 - Information Reply";
            case ICMPPacket.ICMP_MASKREQ:
                return "17 - Address Mask Request";
            case ICMPPacket.ICMP_MASKREPLY:
                return "18 - Address Mask Reply";
            default:
                return String.valueOf(b);
        }

    }

    private DefaultMutableTreeNode analyze(ICMPPacket packet) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode("ICMP");
        this.protocol = "ICMP";
        node.add(new DefaultMutableTreeNode("Type: " + getType(packet.type)));
        node.add(new DefaultMutableTreeNode("Code: " + packet.code));
        node.add(new DefaultMutableTreeNode("ID: " + packet.id));
        node.add(new DefaultMutableTreeNode("Sequence: " + packet.seq));
        return node;
    }

    private String getDataOffset(byte b) {
        return String.valueOf(((b & 0xF0) >> 4) * 4) + " bytes";
    }

    private DefaultMutableTreeNode analyze(TCPPacket packet) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode("TCP");
        this.protocol = "TCP";
        node.add(new DefaultMutableTreeNode("Source port: " + packet.src_port));
        node.add(new DefaultMutableTreeNode("Destination port: " + packet.dst_port));
        node.add(new DefaultMutableTreeNode("Sequence Number: " + packet.sequence));
        node.add(new DefaultMutableTreeNode("Acknowledgement number: " + packet.ack_num));
        node.add(new DefaultMutableTreeNode("Data offset: " + getDataOffset(packet.header[46])));
        DefaultMutableTreeNode flags = new DefaultMutableTreeNode("Flags:");
        flags.add(new DefaultMutableTreeNode("URG: " + packet.urg));
        flags.add(new DefaultMutableTreeNode("ACK: " + packet.ack));
        flags.add(new DefaultMutableTreeNode("PSH: " + packet.psh));
        flags.add(new DefaultMutableTreeNode("RST: " + packet.rst));
        flags.add(new DefaultMutableTreeNode("SYN: " + packet.syn));
        flags.add(new DefaultMutableTreeNode("FIN: " + packet.fin));
        node.add(flags);
        node.add(new DefaultMutableTreeNode("Window: " + packet.window));
        node.add(new DefaultMutableTreeNode("Urgent pointer: " + packet.urgent_pointer));
        return node;
    }

    private DefaultMutableTreeNode analyze(UDPPacket packet) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode("UDP");
        this.protocol = "UDP";
        node.add(new DefaultMutableTreeNode("Source port: " + packet.src_port));
        node.add(new DefaultMutableTreeNode("Destination port: " + packet.dst_port));
        node.add(new DefaultMutableTreeNode("Length: " + packet.length));
        return node;
    }

    private void printTree(PrintWriter pw, DefaultMutableTreeNode node) {
        for (int i = 1; i < node.getLevel(); i++) {
            pw.print("\t");
        }
        pw.println(node.getUserObject());
        if (!node.isLeaf()) {
            for (Enumeration e = node.children(); e.hasMoreElements();) {
                DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement();
                printTree(pw, n);
            }
        }
    }

    /**
     *
     * @param filename
     * @throws java.io.IOException
     */
    public void save(String filename) throws IOException {
        PrintWriter pw = new PrintWriter(new File(filename));
        printTree(pw, rootNode);
        pw.close();
    }

    /**
     *
     * @return
     */
    public String getTCPSocket() {
        if (!(packet instanceof TCPPacket)) {
            return null;
        }
        TCPPacket tcp = (TCPPacket) packet;
        return tcp.src_ip.getHostAddress() + tcp.src_port + tcp.dst_ip.getHostAddress() + tcp.dst_port;
    }
}
